/*
 * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.graalvm.visualizer.coordinator.impl;

import jdk.graal.compiler.graphio.parsing.model.GraphDocument;
import org.openide.LifecycleManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.OnStart;
import org.openide.util.RequestProcessor;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * ONLY for testing/automation purposes. The class will open a BGV file on startup,
 * and will close the application. It will report file open errors in console and
 * through the exit code.
 *
 * @author sdedic
 */
@OnStart
public class FileOpenTestSupport implements Runnable {
    private static final String OPENFILE_PROPERTY = "igv.openfile.onstartup.and.close"; // NOI18N
    private static final int EXIT_SUCCESS = 0;
    private static final int EXIT_NO_FILE = 10;
    private static final int EXIT_DATA_ERROR = 65;  // EX_DATAERR, historical status code for format error.

    @Override
    public void run() {
        String v = System.getProperty(OPENFILE_PROPERTY);
        if (v == null) {
            return;
        }
        int exit = openFile(v);
        // If graceful shutdown does not work, do force-shutdown through TopSecurityManager:
        RequestProcessor.getDefault().post(() -> {
            System.err.println("LifecycleManager did not exit in time, dumping all threads:");
            Exception td = new Exception();
            for (Map.Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
                td.setStackTrace(entry.getValue());
                System.err.print(entry.getKey() + ": ");
                td.printStackTrace();
            }
            System.err.flush();
            SecurityManager smgr = System.getSecurityManager();
            if (smgr != null) {
                try {
                    Method exitMethod = smgr.getClass().getMethod("exit", Integer.TYPE); // NOI18N
                    exitMethod.invoke(smgr, exit);
                } catch (ReflectiveOperationException ex) {
                    ex.printStackTrace();
                }
            } else {
                System.exit(exit);
            }
        }, 5000);
        LifecycleManager.getDefault().exit(exit);
    }

    private int openFile(String v) {
        File f = new File(v);
        FileObject fo = FileUtil.toFileObject(f);
        if (fo == null || !fo.isValid()) {
            System.err.println(String.format("File %s does not exist or is not readable.", f.toString())); // NOI18N
            // the same exit code is returned by OpenFile module.
            return EXIT_NO_FILE;
        }
        if (fo.isData()) {
            return openOneFile(fo);
        }
        for (FileObject ff : fo.getChildren()) {
            if (ff.isData() && "bgv".equalsIgnoreCase(ff.getExt())) { // NOI18N
                int res = openOneFile(ff);
                if (res != EXIT_SUCCESS) {
                    return res;
                }
            }
        }
        return EXIT_SUCCESS;
    }

    private int openOneFile(FileObject fo) {
        // TESTING purposes only: will open the document synchronously, and terminate
        // immediately after that. If the document opens with errors, stacktrace will be printed,
        // and exit status set to != 0.
        File f = FileUtil.toFile(fo);
        try {
            Callable<GraphDocument> c = FileImporter.createDocumentImporter(f.toPath(), false, true, null);
            if (c == null) {
                return EXIT_NO_FILE;
            }
            c.call();
            return EXIT_SUCCESS;
        } catch (Exception ex) {
            ex.printStackTrace();
            return EXIT_DATA_ERROR;
        }
    }
}
